package com.gitee.wsl.compose.chart.geometry.model.simplify

/*
 * Copyright 2012, 2013 Hannes Janetzek
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

import com.gitee.wsl.compose.chart.geometry.model.GeometryBuffer
import com.gitee.wsl.compose.chart.geometry.model.GeometryUtils
import com.gitee.wsl.ext.base.Arrays.arraycopy
import timber.log.Timber


/**
 * Douglas-Peucker line simplification with stack and some bit twiddling.
 *
 * based on: https://github.com/mourner/simplify-js,
 * https://github.com/ekeneijeoma/simplify-java
 */
class SimplifyDP {
    var markers = BooleanArray(128)
    var stack = IntArray(32)
    fun simplify(geom: GeometryBuffer, sqTolerance: Float) {
        val idx = geom.index
        var inPos = 0
        var outPos = 0
        var i = 0
        val n = idx.size
        while (i < n) {
            val len = idx[i]
            if (len < 0) break
            if (len < 6) {
                inPos += len
                outPos += len
                i++
                continue
            }
            val end = simplify(geom.points, inPos, len, outPos, sqTolerance)
            if (end > inPos + len) Timber.e("out larger than cur: {} > {}", end, inPos + len)
            idx[i] = (end - outPos).toShort().toInt()
            outPos = end
            inPos += len
            i++
        }
    }

    fun simplify(points: FloatArray, inPos: Int, length: Int, out: Int, sqTolerance: Float): Int {
        var out = out
        if (length shr 1 >= markers.size) markers = BooleanArray(length shr 1)
        //else
        //	Arrays.fill(markers, false);
        var first = inPos
        var last = inPos + length - 2
        var index = 0
        var maxSqDist: Float
        var sqDist: Float
        var sp = 0
        while (true) {
            maxSqDist = 0f
            var i = first + 2
            while (i < last) {
                sqDist = GeometryUtils.squareSegmentDistance(points, i, first, last)
                if (sqDist > maxSqDist) {
                    index = i
                    maxSqDist = sqDist
                }
                i += 2
            }
            if (maxSqDist > sqTolerance) {
                markers[index - inPos shr 1] = true
                if (sp + 4 == stack.size) {
                    val tmp = IntArray(stack.size + 64)
                    arraycopy(stack, 0, tmp, 0, stack.size)
                    stack = tmp
                }
                stack[sp++] = first
                stack[sp++] = index
                stack[sp++] = index
                stack[sp++] = last
            }
            if (sp == 0) break
            last = stack[--sp]
            first = stack[--sp]
        }
        points[out++] = points[inPos]
        points[out++] = points[inPos + 1]
        last = inPos + length - 2
        for (i in 0 until length / 2) {
            if (!markers[i]) continue
            markers[i] = false
            val pos = inPos + i * 2
            points[out++] = points[pos]
            points[out++] = points[pos + 1]
        }
        points[out++] = points[last]
        points[out++] = points[last + 1]
        return out
    }

    companion object {
        //val log = LoggerFactory.getLogger(SimplifyDP::class.java)
    }
}

